﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Pex.Framework.ComponentModel;
using Microsoft.Pex.Engine.Packages;
using Microsoft.Pex.Engine.ComponentModel;
using Microsoft.Pex.Engine.Domains;
using Microsoft.ExtendedReflection.Metadata.Names;
using Microsoft.ExtendedReflection.ComponentModel;
using Microsoft.ExtendedReflection.Collections;
using Microsoft.ExtendedReflection.Metadata;

namespace HintProvider
{
    public sealed class LocalHintProvider
        : PexExplorationComponentBase, IPexTypeHintProvider, IService
    {
        AssemblyEx currAssembly;
        protected override void Initialize()
        {
            base.Initialize();
            this.ExplorationServices.DomainManager.AddTypeHintProvider(this);
            this.currAssembly = this.Services.CurrentAssembly.Assembly.Assembly;
            this.Log.LogMessage("hint provider", "Hint provider initialized");
        }

        #region IPexTypeHintProvider Members

        public bool TryGetTypeHints(Microsoft.ExtendedReflection.Metadata.TypeEx type,
            out Microsoft.ExtendedReflection.Collections.IIndexable<Microsoft.ExtendedReflection.Metadata.TypeDefinition> hints)
        {
            this.Log.LogMessage("hint provider", "Requested hints for type " + type);

            if (type.FullName.Contains("HintProvider.IVertexListGraph`2"))
            {
                var extendingClassesList = new SafeList<TypeDefinition>();
                foreach (var tdef in this.currAssembly.TypeDefinitions)
                {
                    if (tdef.FullName.Contains("AdjacencyGraph"))
                    {
                        this.Log.LogMessage("hint provider", "Added adjacency graph");
                        extendingClassesList.Add(tdef);
                    }
                }

                this.Log.LogMessage("hint provider", "Returning hints to Pex (" + extendingClassesList.Count + ")");
                hints = extendingClassesList;
                return true;
            }

            hints = null;
            return false;
        }
        #endregion
    }

    public sealed class MyHintProviderAttribute
        : PexComponentElementDecoratorAttributeBase, IPexExplorationPackage
    {        
        #region IPexExplorationPackage Members
        public void AfterExploration(IPexExplorationComponent host, object data)
        {
            
        }

        protected sealed override void Decorate(Name location, IPexDecoratedComponentElement host)
        {
            host.AddExplorationPackage(location, this);            
        }

        public object BeforeExploration(IPexExplorationComponent host)
        {
            return null;
        }

        public void Initialize(IPexExplorationEngine host)
        {
            //This is required to invoke the initialize() method of LocalHintProvider
            var observer = ServiceProviderHelper.GetService<LocalHintProvider>(host);             
        }

        public void Load(Microsoft.ExtendedReflection.ComponentModel.IContainer explorationContainer)
        {
            explorationContainer.AddComponent("HintProvider", new LocalHintProvider());  
        }

        public string Name
        {
            get { return "HintProviderAttribute"; }
        }

        #endregion        
    }
}
